package com.genntii.examManager.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.genntii.examManager.common.constant.CommonConstant;
import com.genntii.examManager.common.exception.ExamIsComplete;
import com.genntii.examManager.common.exception.ExamNotFoundException;
import com.genntii.examManager.common.exception.FinalExamExistsException;
import com.genntii.examManager.common.exception.NoAuthenticationOperateException;
import com.genntii.examManager.common.result.PageResult;
import com.genntii.examManager.domain.entity.Exam;
import com.genntii.examManager.domain.dto.ExamAddDTO;
import com.genntii.examManager.domain.entity.ExamRecord;
import com.genntii.examManager.domain.entity.QuestionBank;
import com.genntii.examManager.domain.entity.Teacher;
import com.genntii.examManager.domain.question.mongo.ExamDetail;
import com.genntii.examManager.domain.question.mongo.QuestionBankMongo;
import com.genntii.examManager.domain.question.vo.*;
import com.genntii.examManager.domain.vo.ExamVO;
import com.genntii.examManager.domain.vo.QuestionBankMongoVO;
import com.genntii.examManager.mapper.*;
import com.genntii.examManager.service.ExamService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;

@Service
@Slf4j
public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements ExamService {

    @Resource
    private MongoTemplate mongoTemplate;

    @Resource
    private ExamMapper examMapper;

    @Resource
    private CourseMapper courseMapper;

    @Resource
    private ExamRecordMapper examRecordMapper;

    @Resource
    private CourseRecordMapper courseRecordMapper;

    @Resource
    private TeacherMapper teacherMapper;

    @Resource
    private SubjectMapper subjectMapper;

    @Resource
    private QuestionBankMapper questionBankMapper;

    @Resource
    private UserMapper userMapper;

    @Resource
    private HttpServletRequest request;

    @Override
    public PageResult<ExamVO> getExamList(Integer pageNum, Integer pageSize, Long userId,Long courseId) {
        ArrayList<Exam> exams = examMapper.getExamList((pageNum-1)*pageSize,pageSize, courseId);
        ArrayList<ExamVO> examVOS = new ArrayList<>();
        ArrayList<Long> examIds = new ArrayList<>();
        for (Exam exam:exams){
            examIds.add(exam.getId());
        }
        ArrayList<ExamRecord> examRecords = examRecordMapper.getExamRecordListByUserExamId((pageNum - 1) * pageSize, pageSize, examIds, userId);

        Map<Long, ExamRecord> examRecordMap = new HashMap<>();
        for (ExamRecord examRecord: examRecords){
            examRecordMap.put(examRecord.getExamId(), examRecord);
        }
        for (Exam exam:exams){
            ExamVO build = ExamVO.builder()
                    .id(exam.getId())
                    .examName(exam.getExamName())
                    .courseName(courseMapper.getCourseById(exam.getCourseId()).getCourseName())
                    .teacherName(teacherMapper.selectById(exam.getTeacherId()).getName())
                    .startTime(exam.getStartTime())
                    .endTime(exam.getEndTime())
                    .continueTime(exam.getContinueTime())
                    .status(exam.getStatus())
                    .isFinalExam(exam.getIsFinalExam())
                    .build();

            if(request.getAttribute(CommonConstant.USER_ID) != null) build.setStudentStatus(examRecordMap.get(exam.getId()).getType() != null ? examRecordMap.get(exam.getId()).getType() : 0);
            examVOS.add(build);
        }
        return PageResult.build(examVOS, examMapper.getExamTotal());
    }

    @Override
    public ExamDetailVO getExam(Long examId, Long userId) {

        if (examMapper.selectById(examId) == null){
            throw new ExamNotFoundException("未找到该课程");
        }
        ExamRecord examHistoryDetial = examRecordMapper.getExamHistoryDetial(examId, userId);
        if (examHistoryDetial != null){
            throw new ExamIsComplete("考试已提交");
        }

        return new ExamDetailVO(Objects.requireNonNull(mongoTemplate.findOne(new Query(new Criteria("_id").is(examId)), ExamDetail.class)));
    }

    @Override
    public void updateStatus(Long examId, Byte status) {
        examMapper.updateStatus(status,examId);
    }

    @Override
    @Transactional
    public void addExam(ExamAddDTO examAddDTO, ArrayList<Long> mongoId, Long userId) {
        Long teacherId = teacherMapper.selectOne(new QueryWrapper<Teacher>().eq("user_id", userId)).getId();
        if (!Objects.equals(courseMapper.selectById(examAddDTO.getCourseId()).getTeacherId(), teacherId)){
            throw new NoAuthenticationOperateException("无权限访问");
        }
        log.info(examAddDTO.getTopicDTO().toString());
        Topic topic = new Topic();
        ArrayList<Long> userIds = courseRecordMapper.getUserIdsByCourse(examAddDTO.getCourseId());
        List<QuestionBankMongo> question = new ArrayList<>();
        int maxScore = 0;
        if (mongoId!=null && !mongoId.isEmpty()){
            if (examAddDTO.getIsFinalExam() == 1 && examMapper.selectById(examAddDTO.getCourseId()).getIsFinalExam() != null){
                throw new FinalExamExistsException("期末考已存在");
            }
            question = mongoTemplate.find(new Query(new Criteria("_id").in(mongoId)), QuestionBankMongo.class);
            Map<Long, Integer> scoreMap = new HashMap<>();

            for (QuestionBankMongo questionBankMongo:question){
                scoreMap.put(questionBankMongo.get_id(),questionBankMongo.getScore());
                maxScore += questionBankMongo.getScore();
                switch (questionBankMongo.getType()){
                    case 1:
                        ArrayList<SingleChoice> singleChoices = topic.getSingleChoices();
                        singleChoices.add(questionBankMongo.getTopic().getSingleChoice());
                        topic.setSingleChoices(singleChoices);
                    case 2:
                        ArrayList<MultipleChoice> multipleChoices = topic.getMultipleChoices();
                        multipleChoices.add(questionBankMongo.getTopic().getMultipleChoice());
                        topic.setMultipleChoices(multipleChoices);
                    case 3:
                        ArrayList<TrueFalse> trueFalses = topic.getTrueFalses();
                        trueFalses.add(questionBankMongo.getTopic().getTrueFalse());
                        topic.setTrueFalses(trueFalses);
                    case 4:
                        ArrayList<Vocabulary> vocabularies = topic.getVocabularies();
                        vocabularies.add(questionBankMongo.getTopic().getVocabulary());
                        topic.setVocabularies(vocabularies);
                }
            }
        }

        if (examAddDTO.getTopicDTO().getMultipleChoices()!=null&&!examAddDTO.getTopicDTO().getMultipleChoices().isEmpty()){
            for (MultipleChoice multipleChoice:examAddDTO.getTopicDTO().getMultipleChoices()){
                ArrayList<MultipleChoice> multipleChoices = new ArrayList<>();
                if (topic.getMultipleChoices() != null && !topic.getMultipleChoices().isEmpty()){
                    multipleChoices = topic.getMultipleChoices();
                }
                multipleChoices.add(multipleChoice);
                maxScore += multipleChoice.getScore();
                topic.setMultipleChoices(multipleChoices);
            }
        }

        if (examAddDTO.getTopicDTO().getSingleChoices()!=null && !examAddDTO.getTopicDTO().getSingleChoices().isEmpty()){
            for (SingleChoice singleChoice:examAddDTO.getTopicDTO().getSingleChoices()){
                ArrayList<SingleChoice> singleChoices = new ArrayList<>();
                if (topic.getSingleChoices() != null && !topic.getSingleChoices().isEmpty()){
                    singleChoices = topic.getSingleChoices();
                }
                singleChoices.add(singleChoice);
                maxScore += singleChoice.getScore();
                topic.setSingleChoices(singleChoices);
            }
        }

        if (examAddDTO.getTopicDTO().getTrueFalses()!=null&&!examAddDTO.getTopicDTO().getTrueFalses().isEmpty()){
            for (TrueFalse trueFalse: examAddDTO.getTopicDTO().getTrueFalses()){
                ArrayList<TrueFalse> trueFalses = new ArrayList<>();
                if (topic.getTrueFalses() != null && !topic.getTrueFalses().isEmpty())
                    trueFalses = topic.getTrueFalses();
                trueFalses.add(trueFalse);
                maxScore += trueFalse.getScore();
                topic.setTrueFalses(trueFalses);
            }
        }

        if (examAddDTO.getTopicDTO().getVocabularies()!=null && !examAddDTO.getTopicDTO().getVocabularies().isEmpty()){
            for (Vocabulary vocabulary:examAddDTO.getTopicDTO().getVocabularies()){
                ArrayList<Vocabulary> vocabularies = new ArrayList<>();
                if (topic.getVocabularies() != null && !topic.getVocabularies().isEmpty())
                    vocabularies = topic.getVocabularies();
                vocabularies.add(vocabulary);
                maxScore += vocabulary.getScore();
                topic.setVocabularies(vocabularies);
            }
        }

        Exam exam = Exam.builder()
                .examName(examAddDTO.getName())
                .courseId(examAddDTO.getCourseId())
                .teacherId(teacherId)
                .startTime(examAddDTO.getStartTime())
                .endTime(examAddDTO.getEndTime())
                .status((byte)1)
                .continueTime(examAddDTO.getContinueTime())
                .isFinalExam(examAddDTO.getIsFinalExam())
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now())
                .build();
        examMapper.insert(exam);
        ExamDetail examDetail = ExamDetail.builder()
                ._id(exam.getId())
                .teacherName(teacherMapper.selectById(teacherId).getName())
                .startTime(exam.getStartTime())
                .endTime(exam.getEndTime())
                .continueTime(exam.getContinueTime())
                .score(maxScore)
                .topic(topic)
                .build();
        if (!question.isEmpty()){
            examDetail.setSubjectName(subjectMapper.selectById(question.get(1).getSubject()).getSubjectName());
        }
        mongoTemplate.insert(examDetail,"examDetail");
        if (!userIds.isEmpty())
            examRecordMapper.insertBatch(exam.getId(),userIds);
    }

    @Override
    public PageResult<QuestionBankMongoVO> getQuestionBankList(Integer pageNum, Integer pageSize, Long subjectId, Byte questionType, String keyword) {
        ArrayList<QuestionBank> questionBanks = questionBankMapper.getQuestionBankListBySubject(subjectId,(pageNum-1)*pageSize,pageSize, keyword, questionType);
        ArrayList<Long> questionIds = new ArrayList<>();
        for(QuestionBank questionBank:questionBanks){
            questionIds.add(questionBank.getId());
        }
        List<QuestionBankMongo> questionBankMongos = mongoTemplate.find(new Query(new Criteria("_id").in(questionIds)), QuestionBankMongo.class);
        ArrayList<QuestionBankMongo> questionBankMongosArr = new ArrayList<>(questionBankMongos);

        ArrayList<QuestionBankMongoVO> questionBankMongoVOS = new ArrayList<>();
        for (QuestionBankMongo questionBankMongo:questionBankMongosArr){
            questionBankMongoVOS.add(QuestionBankMongoVO.builder()
                    .name(questionBankMongo.getName())
                    .type(questionBankMongo.getType())
                    ._id(String.valueOf(questionBankMongo.get_id()))
                    .subject(questionBankMongo.getSubject())
                    .topic(questionBankMongo.getTopic())
                    .score(questionBankMongo.getScore())
                    .build());
        }
        return PageResult.build(questionBankMongoVOS,questionBankMapper.getQuestionBankTotalBySubject(subjectId,keyword,questionType));
    }

    @Override
    public void setFinalExam(Long examId, Long courseId, Long userId) {
        Long teacherId = teacherMapper.selectById(userId).getId();
        if (!Objects.equals(courseMapper.selectById(courseId).getTeacherId(), teacherId)){
            throw new NoAuthenticationOperateException("无权限访问");
        }
        examMapper.setFinalExam(courseId);
        courseMapper.setFinalExam(examId,courseId);
    }
}
